package com.wencai.util;

import com.wencai.bean.PriceAction;
import com.wencai.bean.StockData;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.LinkedHashMap;
import java.util.Optional;

import static com.wencai.util.PriceFinder.getUpLimitPrice;

/**
 * @author wangpan
 * @date 2023/5/13
 */
public class StrategyExecutor {

    private static String getPriceWithPriceAction(StockData stockData, PriceAction priceAction) {
        String price;
        switch (priceAction) {
            case FIVE_MINUTES_OPEN:
                price = String.valueOf(stockData.getOpen());
                break;
            case FIVE_MINUTES_HIGH:
                price = String.valueOf(stockData.getHigh());
                break;
            case FIVE_MINUTES_LOW:
                price = String.valueOf(stockData.getLow());
                break;
            case FIVE_MINUTES_CLOSE:
                price = String.valueOf(stockData.getClose());
                break;
            default:
                return null;
        }
        return price;
    }

    public static String getBuyPrice(LinkedHashMap<String, StockData> map, String key, BuyStrategy buyStrategy, PriceAction priceAction, StockData dailyStockData) {
        StockData indexStockData = map.get(key);
        if (indexStockData == null) {
            return null;
        }
        switch (buyStrategy.getBuyStrategyType()) {
            case NORMAL:
                return getPriceWithPriceAction(indexStockData, priceAction);
            case SKIP_LIMIT_UP:
                return getSkipLimitUpPrice(map, key, priceAction);
            case LIMIT_UP_IF_TOUCHED:
                return getLimitUpIfTouched(map, key, priceAction, dailyStockData);
            case MAX:
                return getMax(map, key, priceAction);
            case SKIP_HIGHER:
                return null;
            case SKIP_LOWER:
                return null;
            case BUY_AT_HIGHER_OTHERS_SKIP:
            case BUY_AT_LOWER_OTHERS_SKIP:
            case BUY_AT_HIGHER_OTHERS_NORMAL:
            case BUY_AT_LOWER_OTHERS_NORMAL:
            case BUY_AT_FINAL_HIGHER_OTHERS_SKIP:
            case BUY_AT_FINAL_LOWER_OTHERS_SKIP:
        }
        return null;
    }


    private static String getMax(LinkedHashMap<String, StockData> map, String key, PriceAction priceAction) {
        return "" + map.values().stream().mapToDouble(StockData::getHigh).max().getAsDouble();
    }

    private static String getLimitUpIfTouched(LinkedHashMap<String, StockData> map, String key, PriceAction priceAction, StockData dailyStockData) {
        double preClose = dailyStockData.getPreClose();
        double upLimitPrice = getUpLimitPrice(preClose);
        double dailyHigh = map.values().stream().mapToDouble(StockData::getHigh).max().getAsDouble();
        if (dailyHigh == upLimitPrice) {
            return dailyHigh + "";
        }
        return getPriceWithPriceAction(map.get(key), priceAction);
    }

    /**
     * 如果在设置的时间后, 价格的最高/最低/开盘/收盘价均为同一个价格, 则说明一直维持涨停, 如果在买入时间点之后一直封住涨停, 则直接跳过
     *
     * @return
     */
    public static String getSkipLimitUpPrice(LinkedHashMap<String, StockData> map, String key, PriceAction priceAction) {
        int keyIndex = MapUtils.getKeyIndex(map, key);
        LinkedHashMap<String, StockData> subAfterMap = MapUtils.subAfterMap(map, keyIndex, true);
        double dailyHigh = map.values().stream().mapToDouble(StockData::getHigh).max().getAsDouble();
        double maxHigh = subAfterMap.values().stream().mapToDouble(StockData::getHigh).max().getAsDouble();
        double minLow = subAfterMap.values().stream().mapToDouble(StockData::getLow).min().getAsDouble();
        // 如果最高的最高价等于最低的最低价且最高的最高价大于等于全天最高价, 则当前为涨停状态
        if (maxHigh == minLow && maxHigh >= dailyHigh) {
            return null;
        }
        return getPriceWithPriceAction(map.get(key), priceAction);
    }


    public static String getSellPrice(LinkedHashMap<String, StockData> map, String key, SellStrategy sellStrategy, PriceAction priceAction, StockData dailyStockData) {
        StockData indexStockData = map.get(key);
        if (indexStockData == null) {
            return null;
        }
        switch (sellStrategy.getSellStrategyType()) {
            // 默认情况下定时卖出
            case NORMAL:
                return getPriceWithPriceAction(indexStockData, priceAction);
            // 如果限定了止盈涨幅, 则在限定时间之前价格超过最大涨幅时卖出, 如果未触发, 则在限定时间卖出
            case HIGHER:
                // 1. 获取昨日收盘价, 计算限定涨幅时对应的价格
                return getHigherValueIfPossible(map, key, sellStrategy, priceAction, dailyStockData, indexStockData);
            case LOWER:
                // 1. 获取昨日收盘价, 计算限定涨幅时对应的价格
                return getLowerValueIfPossible(map, key, sellStrategy, priceAction, dailyStockData, indexStockData);
            case HIGHER_OR_LOWER:
                // 如果同时设置了止盈涨幅和止损涨幅, 则当限定时间之前达到止盈或止损时, 价格超过止盈或止损则卖出, 如果未触发, 则在限定时间卖出
                return getHigherOrLowerIfPossible(map, key, sellStrategy, priceAction, dailyStockData, indexStockData);
            default:
                return null;
        }
    }

    // 大于上限或者低于下限时的卖出策略
    private static String getHigherOrLowerIfPossible(LinkedHashMap<String, StockData> map, String key, SellStrategy sellStrategy, PriceAction priceAction, StockData dailyStockData, StockData indexStockData) {
        double preClose = dailyStockData.getPreClose();
        double lowerPrice = ((100 + sellStrategy.getLowerValue()) * preClose) / 100;
        double higherPrice = ((100 + sellStrategy.getHigherValue()) * preClose) / 100;
        double lowLimitPrice = PriceFinder.getLowLimitPrice(preClose);
        double highLimitprice = getUpLimitPrice(preClose);
        lowerPrice = lowerPrice < lowLimitPrice ? lowLimitPrice : lowerPrice;
        higherPrice = higherPrice > highLimitprice ? highLimitprice : lowerPrice;
        // 获取限定时间之前, 最大价格
        int keyIndex = MapUtils.getKeyIndex(map, key);
        boolean includingIndex = !priceAction.equals(PriceAction.FIVE_MINUTES_OPEN);
        LinkedHashMap<String, StockData> aheadMap = MapUtils.subAheadMap(map, keyIndex, includingIndex);
        if (aheadMap.isEmpty()) {
            return getPriceWithPriceAction(indexStockData, priceAction);
        }
        // 如果开盘价就比设置的下限低或者比设置的上限高, 则直接卖出
        StockData stockData = (StockData) map.values().toArray()[0];
        if (stockData.getOpen() <= lowerPrice || stockData.getOpen() >= higherPrice) {
            return String.valueOf(stockData.getOpen());
        }
        // 查询最低价, 如果最大价格小于限定价格, 则成交价设置为限定价格, 否则成交价设置为对应时间价格
        // 五分钟内同时打止盈止损,以止盈计算, 后续可能修改设置 TODO
        for (StockData sd : map.values()) {
            if (sd.getHigh() >= higherPrice) {
                return String.valueOf(higherPrice);
            }
            if (sd.getLow() <= lowerPrice) {
                return String.valueOf(lowerPrice);
            }
        }
        return getPriceWithPriceAction(indexStockData, priceAction);
    }

    private static String getLowerValueIfPossible(LinkedHashMap<String, StockData> map, String key, SellStrategy sellStrategy, PriceAction priceAction, StockData dailyStockData, StockData indexStockData) {
        double preClose = dailyStockData.getPreClose();
        double limitPrice = ((100 + sellStrategy.getLowerValue()) * preClose) / 100;
        double lowLimitPrice = PriceFinder.getLowLimitPrice(preClose);
        limitPrice = limitPrice < lowLimitPrice ? lowLimitPrice : limitPrice;
        // 获取限定时间之前, 最大价格
        int keyIndex = MapUtils.getKeyIndex(map, key);
        boolean includingIndex = !priceAction.equals(PriceAction.FIVE_MINUTES_OPEN);
        LinkedHashMap<String, StockData> aheadMap = MapUtils.subAheadMap(map, keyIndex, includingIndex);
        if (aheadMap.isEmpty()) {
            return getPriceWithPriceAction(indexStockData, priceAction);
        }
        // 如果开盘价就比设定值低, 则直接卖出
        StockData stockData = (StockData) map.values().toArray()[0];
        if (stockData.getOpen() <= limitPrice) {
            return String.valueOf(stockData.getOpen());
        }
        // 查询最低价, 如果最大价格小于限定价格, 则成交价设置为限定价格, 否则成交价设置为对应时间价格
        double aheadMin = aheadMap.values().stream().mapToDouble(item -> item.getLow()).min().getAsDouble();
        if (aheadMin < limitPrice) {
            return String.valueOf(limitPrice);
        }
        return getPriceWithPriceAction(indexStockData, priceAction);
    }

    private static String getHigherValueIfPossible(LinkedHashMap<String, StockData> map, String key, SellStrategy sellStrategy, PriceAction priceAction, StockData dailyStockData, StockData indexStockData) {
        double preClose = dailyStockData.getPreClose();
        double limitPrice = ((100 + sellStrategy.getHigherValue()) * preClose) / 100;
        limitPrice = new BigDecimal(limitPrice).setScale(2, RoundingMode.UP).doubleValue();

        double upLimit = getUpLimitPrice(preClose);
        limitPrice = limitPrice > upLimit ? upLimit : limitPrice;
        // 获取限定时间之前, 最大价格
        int keyIndex = MapUtils.getKeyIndex(map, key);
        boolean includingIndex = !priceAction.equals(PriceAction.FIVE_MINUTES_OPEN);
        LinkedHashMap<String, StockData> aheadMap = MapUtils.subAheadMap(map, keyIndex, includingIndex);
        if (aheadMap.isEmpty()) {
            return getPriceWithPriceAction(indexStockData, priceAction);
        }
        // 如果开盘价就比限定价高, 则直接卖出
        StockData stockData = (StockData) map.values().toArray()[0];
        if (stockData.getOpen() >= limitPrice) {
            return String.valueOf(stockData.getOpen());
        }
        // 查询最高价, 如果最大价格大于限定价格, 则成交价设置为限定价格, 否则成交价设置为对应时间价格
        double finalLimitPrice = limitPrice;
        Optional<StockData> first = aheadMap.values().stream().filter(item -> item.getHigh() >= finalLimitPrice).findFirst();
        if (first.isPresent()) {
            // 此时卖出时间为实际时间
            String time = first.get().getDate();
            return String.valueOf(limitPrice);
        }
        return getPriceWithPriceAction(indexStockData, priceAction);
    }
}
